package ynu.edu.user.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import ynu.edu.api.client.PermissionFeignClient;
import ynu.edu.common.constant.PermissionConstants;
import ynu.edu.common.domain.PageDTO;
import ynu.edu.common.domain.R;
import ynu.edu.common.exception.BadRequestException;
import ynu.edu.common.exception.BizIllegalException;
import ynu.edu.common.exception.DbException;
import ynu.edu.common.exception.ForbiddenException;
import ynu.edu.common.utils.BeanUtils;
import ynu.edu.common.utils.RedisCache;
import ynu.edu.common.utils.UserContext;
import ynu.edu.user.config.JwtProperties;
import ynu.edu.user.domain.dto.LoginFormDTO;
import ynu.edu.user.domain.po.User;
import ynu.edu.user.domain.vo.UserLoginVO;
import ynu.edu.user.domain.vo.UserQuery;
import ynu.edu.user.domain.vo.UserVO;
import ynu.edu.user.enums.UserStatus;
import ynu.edu.user.mapper.UserMapper;
import ynu.edu.user.service.IUserService;
import ynu.edu.user.util.JwtTool;


import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;


@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    private final PasswordEncoder passwordEncoder;

    private final JwtTool jwtTool;

    private final JwtProperties jwtProperties;

    private final UserMapper userMapper;

    private final RedisCache redisCache;

    private final PermissionFeignClient permissionFeignClient;

    @Override
    public R<UserLoginVO> login(LoginFormDTO loginDTO) {
        // 1.数据校验
        String username = loginDTO.getUsername();
        String password = loginDTO.getPassword();
        // 2.根据用户名或手机号查询
        User user = lambdaQuery().eq(User::getUsername, username).one();
        Assert.notNull(user, "用户名错误");
        // 3.校验是否禁用
        if (user.getStatus() == UserStatus.FROZEN) {
            throw new ForbiddenException("用户被冻结");
        }
        // 4.校验密码
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new BadRequestException("用户名或密码错误");
        }

        Long userId = user.getId();

        // 5.查询角色和权限
//        List<String> roles = permissionFeignClient.getRoles(userId); // 查询角色
//        List<String> permissions = permissionFeignClient.getPermissions(userId); // 查询权限

        try {
            CompletableFuture<List<String>> rolesFuture = CompletableFuture.supplyAsync(() -> permissionFeignClient.getRoles(userId));
            CompletableFuture<List<String>> permsFuture = CompletableFuture.supplyAsync(() -> permissionFeignClient.getPermissions(userId));

            // 等待所有任务完成，并提取结果
            List<String> roles = rolesFuture.get();
            List<String> permissions = permsFuture.get();

            // 后续业务逻辑...
            // 6.缓存权限到Redis（
            String redisKey = PermissionConstants.PERMISSION_CACHE_PREFIX+ userId;
            Duration ttl = Duration.ofDays(1).plusMinutes(new Random().nextInt(60));
            redisCache.setCacheObject(redisKey, permissions, ttl);

            // 7.生成TOKEN
            String token = jwtTool.createToken(userId, jwtProperties.getTokenTTL());

            // 8.封装VO返回
            UserLoginVO vo = new UserLoginVO();
            vo.setUserId(userId);
            vo.setUsername(user.getUsername());
            vo.setToken(token);
            vo.setRoles(roles);
            vo.setPermissions(permissions);

            UserContext.setUser(userId);
            log.info("用户[{}]登录成功", userId);

            return R.ok(vo);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("任务被中断", e);
            return R.error("系统繁忙，请重试");
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof ForbiddenException) {
                throw (ForbiddenException) cause;  // 透传业务异常
            } else if (cause instanceof BadRequestException) {
                throw (BadRequestException) cause;
            } else {
                log.error("服务调用失败", cause);
                return R.error("系统服务暂不可用");
            }
        }

//        // 6.缓存权限到Redis（
//        String redisKey = PermissionConstants.PERMISSION_CACHE_PREFIX+ userId;
//        Duration ttl = Duration.ofDays(1).plusMinutes(new Random().nextInt(60));
//        redisCache.setCacheObject(redisKey, permissions, ttl);
//
//        // 7.生成TOKEN
//        String token = jwtTool.createToken(userId, jwtProperties.getTokenTTL());
//
//        // 8.封装VO返回
//        UserLoginVO vo = new UserLoginVO();
//        vo.setUserId(userId);
//        vo.setUsername(user.getUsername());
//        vo.setToken(token);
//        vo.setRoles(roles);
//        vo.setPermissions(permissions);
//
//        UserContext.setUser(userId);
//        log.info("用户[{}]登录成功", userId);
//
//        return R.ok(vo);
    }

    /**
     * 扣款
     * @param totalFee
     */
    @Override
    public void deductMoney(Long userId ,Integer totalFee) {
        log.info("开始扣款，用户ID: {}, 金额: {}",userId, totalFee);
        
        // 参数验证
        if (totalFee == null || totalFee <= 0) {
            log.error("支付金额无效: {}", totalFee);
            throw new BadRequestException("支付金额必须大于0");
        }
        
        // 1.查询用户信息
        User user = getById(userId);
        if (user == null) {
            log.error("用户不存在，用户ID: {}", userId);
            throw new BizIllegalException("用户不存在");
        }
        
        // 2.检查余额是否足够
        if (user.getBalance() < totalFee) {
            log.error("余额不足，用户ID: {}, 当前余额: {}, 需要扣减: {}", 
                    userId, user.getBalance(), totalFee);
            throw new BizIllegalException("余额不足");
        }
        
        // 3.尝试扣款
        try {
            baseMapper.updateMoney(userId, totalFee);
            log.info("扣款成功，用户ID: {}, 扣减金额: {}, 剩余余额: {}", 
                    userId, totalFee, user.getBalance() - totalFee);
        } catch (Exception e) {
            log.error("扣款失败，用户ID: {}, 金额: {}", userId, totalFee, e);
            throw new RuntimeException("扣款失败，可能是余额不足！", e);
        }
    }

    /**
     * 注册
     *
     * @param loginFormDTO
     * @return
     */
    @Override
    public R<String> register(LoginFormDTO loginFormDTO) {
        //1.校验用户名是否重复
        int count = lambdaQuery()
                .eq(User::getUsername, loginFormDTO.getUsername())
                .count();
        if (count > 0) {
            throw new BizIllegalException("用户名已被注册");
        }
        //2.保存用户信息
        User user = new User();
        user.setUsername(loginFormDTO.getUsername());
        user.setPassword(passwordEncoder.encode(loginFormDTO.getPassword()));
        user.setStatus(UserStatus.NORMAL);
        user.setBalance(0);
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());

        boolean saveResult = save(user);
        if (!saveResult) {
            throw new BizIllegalException("用户注册失败");
        }

        // 3. 赋默认角色（添加异常处理）
        try {
            permissionFeignClient.assignDefaultRole(user.getId()); // 使用正确的用户ID
        } catch (Exception e) {
            log.error("用户[{}]角色分配失败: {}", user.getId(), e.getMessage());
            throw new BizIllegalException("角色分配失败");
        }
        return R.ok("注册成功");
    }

    /**
     * 分页查询用户列表
     * @param query
     * @return
     */
    @Override
    public R<PageDTO<UserVO>> queryUserByPage(UserQuery query) {
        try {
            // 1.构建查询条件
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.like(StrUtil.isNotBlank(query.getUsername()), User::getUsername, query.getUsername())
                    .like(StrUtil.isNotBlank(query.getPhone()), User::getPhone, query.getPhone())
                    .eq(query.getStatus() != null, User::getStatus, query.getStatus());

            // 2.分页查询
            Page<User> page = userMapper.selectPage(query.toMpPageDefaultSortByCreateTimeDesc(), wrapper);

            // 3.转换结果
            return R.ok(PageDTO.of(page, UserVO.class));
        } catch (Exception e) {
            throw new DbException("查询用户列表失败", e);
        }
    }

    /**
     * 查询用户详情
     * @param id
     * @return
     */
    @Override
    public R<UserVO> queryUserById(Long id) {
        try {
            // 1.查询用户
            User user = userMapper.selectById(id);
            if (user == null) {
                throw new BizIllegalException("用户不存在");
            }

            // 2.转换
            return R.ok(BeanUtils.copyBean(user, UserVO.class));
        } catch (BizIllegalException e) {
            throw e;
        } catch (Exception e) {
            throw new DbException("查询用户详情失败", e);
        }
    }

    /**
     * 修改用户状态
     * @param id
     * @param status
     */
    @Override
    public void updateUserStatus(Long id, Integer status) {
        try {
            // 1.参数校验
            if (id == null) {
                throw new BadRequestException("用户ID不能为空");
            }
            if (status == null) {
                throw new BadRequestException("状态不能为空");
            }
            if (status != 1 && status != 2) {
                throw new BadRequestException("状态值不合法");
            }

            // 2.查询用户
            User user = userMapper.selectById(id);
            if (user == null) {
                throw new BizIllegalException("用户不存在");
            }

            // 3.更新状态
            User updateUser = new User();
            updateUser.setId(id);
            updateUser.setStatus(UserStatus.of(status));
            updateUser.setUpdateTime(LocalDateTime.now());
            int rows = userMapper.updateById(updateUser);
            if (rows != 1) {
                throw new DbException("更新用户状态失败");
            }
        } catch (BadRequestException | BizIllegalException e) {
            throw e;
        } catch (Exception e) {
            throw new DbException("更新用户状态失败", e);
        }
    }

    /**
     * 更新用户信息
     * @param id
     * @param userVO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUser(Long id, UserVO userVO) {
        // 1. 查询用户是否存在
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new BizIllegalException("用户不存在");
        }

        // 2. 更新用户信息
        User updateUser = new User();
        updateUser.setId(id);
        updateUser.setUsername(userVO.getUsername());
        updateUser.setPhone(userVO.getPhone());
        updateUser.setStatus(UserStatus.of(userVO.getStatus()));
        updateUser.setUpdateTime(LocalDateTime.now());

        // 3. 执行更新
        int rows = userMapper.updateById(updateUser);
        if (rows != 1) {
            throw new DbException("更新用户失败");
        }
    }

    /**
     * 登出
     */
    @Override
    public void logout() {
        // 获取当前用户ID
        Long userId = UserContext.getUser();
        if (userId == null) {
            log.info("用户未登录或会话已过期");
        }
        //不用清理ThreadLocal，因为拦截器当请求结束后会自动清理
        log.info("用户{}退出登录", userId);
    }

    /**
     * 修改密码
     * @param oldPw
     * @param newPw
     */
    @Override
    public void updatePassword(String oldPw, String newPw) {
        // 参数校验
        if (oldPw == null || newPw == null) {
            throw new BadRequestException("密码不能为空");
        }
        if (oldPw.equals(newPw)) {
            throw new BadRequestException("新旧密码不能相同");
        }

        Long userId = UserContext.getUser();
        User user = userMapper.selectById(userId);
        // 检查用户是否存在
        if (user == null) {
            throw new BadRequestException("用户不存在");
        }
        // 校验旧密码
        if (!passwordEncoder.matches(oldPw, user.getPassword())) {
            throw new BadRequestException("旧密码错误");
        }
        // 更新密码
        String encryptedPassword = passwordEncoder.encode(newPw);
        User updatedUser = new User();
        updatedUser.setId(userId);
        updatedUser.setPassword(encryptedPassword);
        userMapper.updateById(updatedUser);
    }


}
